/*
 ============================================================================
 Name        : Assembler.cpp
 Author      : 
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C++,
 ============================================================================
 */

#include <iostream>
#include "AssemblerClasses.hh"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <limits.h>
#include <assert.h>

# include <err.h>
# include <fcntl.h>
# include <libelf.h>
# include <stdio.h>
# include <stdlib.h>
# include <sysexits.h>
# include <unistd.h>

void WriteToHex(AssemblerData s,char *d);

void WriteToElf(AssemblerData s,char *d,int argc , char ** argv);

void WriteToBin(AssemblerData s,char *d);

unsigned char * CreateDataString(unsigned int a);

unsigned char * CreateDataLine(unsigned char *s);

unsigned char * CreateDataLineNonStandard(unsigned char *s,int size);

void CreateFile(unsigned char **s,char *d);

unsigned char *CreateEOF();


unsigned char * Checksum(unsigned char *s);

unsigned char * CheckData(unsigned char *rawhex);

void WriteInstruction(AssemblerData s,char * d);

void WriteElfData(AssemblerData s,char *d);

void CreateElf(char *s);

int elf_setshstrndx(Elf *e, size_t strndx);

uint32_t hash_words [] =
{
0x01234567 ,0x89abcdef ,0xdeadc0de
};

char string_table [] =
{
/* Offset 0 */ '\0 ',
/* Offset 1 */ '.', 'f', 'o', 'o',  '\0',
/* Offset 6 */ '.', 's', 'h', 's', 't',
'r', 't', 'a', 'b','\0'
};


static int address=0000;

int main(int argc , char ** argv)
{
/*
 * could pass a refrence to a variable instead of
 */

	/* the assemblerData*/
	/*
	 * class AssemblerData //Stores a list of assembly commands, a list of comments, and a list of data.
	{
	vector<AssemblyCommands> * data; //Where the assembly commands will be stored.
	vector<Comments> * comments; //Where the comments will be stored.
	vector<unsigned int> * mc_data; //Where the machine code data will be stored.
	vector<Errors> * errors; //Where the errors that are encountered will be stored.

	unsigned int ln; //The number of lines in the assembly file (or the current line position when going through the assembly file).

	public:
	 */

	AssemblerData structure;

	/*appends data*/
	structure.mcData_append(325435);
	structure.mcData_append(4534083);
	structure.mcData_append(243085);
	structure.mcData_append(2334);
	structure.mcData_append(25325);
	structure.mcData_append(987);
	structure.mcData_append(4656);
	structure.mcData_append(7543245);
	structure.mcData_append(983576);


	 //Appends a comment to "comments"

	string f ("st");
	string test1 ("sup");
	string test2("my");
	string test3 ("home");
	string test4 ("nigga");

	structure.com_append(test1,1,f);

	structure.com_append(test2,2,f);
	structure.com_append(test3,3,f);
	structure.com_append(test4,4,f);



	int loop=1;
	while(loop==1)
	{
		/* doesnt take any name it needs an imput with  a .bin also doesnt check if a file exists before
		 * opening
		 * */
			char *s;
			s= (char *)malloc(250*sizeof(char));
			int length;
			printf("Output File Name:");
			scanf("%s", s);

			/*doesnt have the .elf /.hex / .bin */
			length = strlen(s);
			if(length<4)
			{
					perror("Missing the extension:");
			}
			else
			{


				/*.bin .hex .elf */
    			char *extension;
    			extension=(char *)malloc(4*sizeof(char));
    			/*doesnt free extension*/


    			/*sets the extension equal to the proper value*/
    			char * checking=&s[length-3];


    			/*
    			strcat(extension,checking);
    			*/
    			extension=checking;

    			/*the hex file*/
    			if(strncmp(extension,"hex",3)==0)
    			{

    						WriteToHex(structure,s);
    						loop=2;
    						free(s);


    			}

    			/*elf file*/
    			else if(strncmp(extension,"elf",3)==0)
    			{
					WriteToElf(structure,s,argc , argv);
					loop=2;
					free(s);
   				}

    			/*bin file*/
    			else if(strncmp(extension,"bin",3)==0)
    			{
					WriteToBin(structure,s);
					loop=2;

    						free(s);
    			}


    			else
    			{
    				/*the input file was not found*/
    					perror("Invalid input extension not excepted (case matters)");
    	  	 	 	 perror("Please input another file name extensions accepted ; .elf .hex. bin ");

    			}


    			/*IF END*/
			}


    	/*else end*/
    }
    /*while end*/


	return 0;
}
void WriteToHex(AssemblerData s,char *f)
{
	unsigned char **Hexlines;
	int linecount=0;


	/*
	 *
:10246200464C5549442050524F46494C4500464C33
|||||||||||                              CC->Checksum
|||||||||DD->Data
|||||||TT->Record Type
|||AAAA->Address
|LL->Record Length
:->Colon

	 */
	vector<unsigned int> * m_data;

	m_data=s.ret_mcData();

	unsigned char *data;


	Hexlines=(unsigned char **)malloc((unsigned int)s.mcData_len()*(sizeof(unsigned char)));
	data=(unsigned char *)malloc((unsigned int)s.mcData_len()*16*(sizeof( char)));

	int i;

	int sizeofvector= (int)s.mcData_len();

	for(i=0;i < sizeofvector;i++)
	{

		unsigned int singledata;

		singledata=m_data->at(i);

		strcat( (char*)data,(char*)  CreateDataString(singledata) );

	}

	/*
	 * data contains all the data in hex form as characters
	 */

	/*create individual lines of code*/
	i=0;

	/*int is 4 bytes 2 characters is one one byte*/ /*hex file has a 32 char*/ /*one int is 8 Hex char*/
	/*
	 * per line you have 4 ints for standard 10 bytes
	 */
	int standard_size=strlen((char*)data)/32;
	int numberleftover=strlen((char*)data)%32;
	/*default record of 4 ints  creation*/
	while(i<standard_size)
	{
		unsigned char *input;
		input=(unsigned char*)malloc(32*sizeof(unsigned char));
		unsigned char * temp;
		temp=&data[i];
		memcpy(input, temp, 32 );
		Hexlines[linecount]=CreateDataLine(input);
		linecount++;
		i=i+32;

	}

	/*leftover*/

	    unsigned char *input;
		/*sets it to the last values*/
		input=&data[i];
		Hexlines[linecount]=CreateDataLineNonStandard(input,numberleftover);
		linecount++;





	/*EndofFile*/
		Hexlines[linecount]=CreateEOF();
		linecount++;
		Hexlines[linecount]=0;


	/*writes the files to hexfile*/
		CreateFile(Hexlines,f);

}
void WriteToElf(AssemblerData x,char *s,int argc , char ** argv)
{
	 CreateElf(s);
	 /*

	WriteInstruction(x,s);

	 WriteElfData(x,s);
	 */


}
void WriteToBin(AssemblerData s,char *f)
{
	vector<unsigned int> * m_data;
	m_data=s.ret_mcData();
	FILE * pFile;


	  pFile = fopen ( f , "wb" );

	  if(pFile)
		  {

		  	  int i=0;
		  	  int check=(int)s.mcData_len();
		  	  while(i <check)
		  	  {
		  		  fprintf(pFile,"%d",m_data->at(i));
		  		  i++;
		  	  }
		  }
		  else
		  {
			  perror("couldnt open a file wtf");
		  }



}

unsigned char * CreateDataString(unsigned int a)
{
	unsigned char *s=(unsigned char *)malloc(8*sizeof(unsigned char));
	sprintf((char *)s,"%X",a);
	return s;
}

unsigned char * CreateDataLine(unsigned char *datastring)
{
	char *buffer=(char *)malloc(4*sizeof(char));
	sprintf(buffer,"%d",address);
	/*
	itoa(address,buffer,10);
	*/
	address++;
	unsigned char *output=(unsigned char *)malloc(80*sizeof(unsigned char));
	char * prelimenary=":10";/*:LL-> (Record Length after this /4 )*/
	char * afterward="00";/*TT->Record Type*/

	/*adds :10*/
	strcat((char *)output,prelimenary);

	/*adds the address*/
	strcat((char *)output,"000");

	strcat((char *)output,buffer);

	/*adds the data type*/
	strcat((char *)output,afterward);

	/*adds the data string*/
	strcat((char *)output,(char *)datastring);

	/*creates the checksum*/
	unsigned char *checksum=(unsigned char *)Checksum((unsigned char *)datastring);

	/*creates the final output*/
	strcat((char *)output,(char *)checksum);

	free(buffer);
	return output;
}

unsigned char * CreateDataLineNonStandard(unsigned char *s,int siz)
{
		char *buffer=(char *)malloc(4*sizeof(char));
		sprintf(buffer,"%d",address);
		/*
		itoa(address,buffer,10);
		*/
		unsigned char * datastring;

		datastring=s;

		address++;
		unsigned char *output=(unsigned char *)malloc(80*sizeof(unsigned char));

		char *size =(char *)malloc(2*sizeof(char));

		sprintf(size,"%d",siz);
		/*
		itoa(size,siz,10);
		*/
		char * prelimenary=":";/*:LL-> (Record Length after this /4 )*/

		char * afterward="00";/*TT->Record Type*/

		/*adds :*/
		strcat((char *)output,prelimenary);

		/*adds the zero if applicable*/
		strcat((char *)output,"0");

		/*adds length*/
		strcat((char *)output,size);


		/*adds the address*/
		strcat((char *)output,"000");

		strcat((char *)output,buffer);

		/*adds the data type*/
		strcat((char *)output,afterward);

		/*adds the data string*/
		strcat((char *)output, (char*)datastring);

		/*creates the checksum*/
		char *checksum=(char *)Checksum(datastring);

		/*creates the final output*/
		strcat((char *)output,checksum);

		free(buffer);
		return output;
}

unsigned char *CreateEOF()
{
	unsigned char *output;
	output=(unsigned char *)":00000001FF";

	return output;
}

void CreateFile(unsigned char **h,char *s)
{

	  FILE * pFile;

	  unsigned char ** g = h;


	  pFile = fopen ( s , "w" );

	  if(pFile)
	  {

	  	  int i;
	  	  while(g[i] !=0)
	  	  {
	  		  fprintf(pFile,"%s",g[i]);
	  		  fprintf(pFile,"%s","\n");
	  		  i++;
	  	  }
	  }
	  else
	  {
		  perror("couldnt open a file wtf");
	  }


}

unsigned char * Checksum(unsigned char *line)
{


		unsigned char *addition;

		addition=CheckData(line);
		int x=0;
		unsigned char z;
		int i=0;
		unsigned char * str;
		str=(unsigned char *)malloc(3*sizeof(unsigned char));


		/* truncates and doesnt add up*/
		while((int) i< (int)strlen((char*)addition))
		{
			/*truncate the char value*/
			unsigned char c;
			c=addition[i];
			x = x+ c;
			i++;
		}


		z=(unsigned char)(x%256)-1;
		sprintf((char *)str,"%X",z);

		return str;
}

unsigned char * CheckData(unsigned char *rawhex)
{
	unsigned char *x;
	/*after colon*/
	int i=1;
	int j=0;
	unsigned char c,d;
	int length = strlen((char*)rawhex);
	x=(unsigned char *)malloc(length*sizeof(unsigned char));
	int oper;
	unsigned char final;
	while(i<length-2)
	{
		/*
		1234567 8 9 10
		imax=9 =10
		length =10
		imax=8=9
		length =8*/

		oper = 0;
		c=rawhex[i];
		d=rawhex[i+1];
		/* take c or with zero right shift 4 bits take resultant and or with d */
		/*syntax
		 * x = y << 4;
		 * 0 | c
		 *
		 *
		 */

		if ('0' <= c && c <= '9')
		{
			if ('0' <= d && d <= '9')
					{


							d = d-'0';


					}
					else if ('A' <= d && d <= 'F')
					{

								d=d-55;

					}

			c = c-'0';

		}
		else if ('A' <= c && c <= 'F')
		{
					if ('0' <= d && d <= '9')
							{

									d = d-'0';


							}
							else if ('A' <= d && d <= 'F')
							{

										d=d-55;

							}
					c=c-55;
		}

		if(c==0 && d==0)
		{
			i=i+2;
		}
		else{
				 final =c|oper;
				 final =final<<4;
				 final =d|final;
				 x[j]=final;
				 i=i+2;
				 j++;
		}



	}
	return x;

}

void CreateElf(char *s,int argc , char ** argv )
{
	int fd;
	Elf *e;
	Elf_Scn *scn ;
	Elf_Data * data ;
	Elf32_Ehdr * ehdr ;
	Elf32_Phdr * phdr ;
	Elf32_Shdr * shdr ;

	if ( argc != 2)
	{
		errx ( EX_USAGE , " usage : %s file - name ", argv [0]);
	}


	if ( elf_version ( EV_CURRENT ) == EV_NONE )
	{
		errx ( EX_SOFTWARE , " ELF  library   initialization  "		" failed : %s", elf_errmsg ( -1));
	}


	if (( fd = open ( argv [1] , O_WRONLY | O_CREAT , 0777)) < 0)
	{
			err ( EX_OSERR , " open  \%s\"  failed ", argv [1]);
	}

	if ((e = elf_begin (fd , ELF_C_WRITE , NULL )) == NULL )
	{
		errx ( EX_SOFTWARE , " elf_begin ()  failed : %s.",
		elf_errmsg ( -1));
	}
	if (( ehdr = elf32_newehdr (e)) == NULL )
	{
		errx ( EX_SOFTWARE , " elf32_newehdr ()  failed : %s.",
			elf_errmsg ( -1));
	}

	ehdr -> e_ident [ EI_DATA ] = ELFDATA2MSB ;
	ehdr -> e_machine = EM_PPC ; /* 32- bit PowerPC object */
	ehdr -> e_type = ET_EXEC ;
	if (( phdr = elf32_newphdr (e, 1)) == NULL )
	{
		errx ( EX_SOFTWARE , " elf32_newphdr ()  failed : %s.",
	elf_errmsg ( -1));
	}

	if (( scn = elf_newscn (e)) == NULL )
	{
		errx ( EX_SOFTWARE , " elf_newscn ()  failed : %s.",
	elf_errmsg ( -1));
	}

	if (( data = elf_newdata ( scn )) == NULL )
	{
		errx ( EX_SOFTWARE , " elf_newdata ()  failed : %s.",
	elf_errmsg ( -1));
	}

	data -> d_align = 4;
	data -> d_off = 0LL;
	data -> d_buf = hash_words ;
	data -> d_type = ELF_T_WORD ;
	data -> d_size = sizeof ( hash_words );
	data -> d_version = EV_CURRENT ;
	if (( shdr = elf32_getshdr ( scn )) == NULL )
	errx ( EX_SOFTWARE , " elf32_getshdr ()  failed : %s.",
	elf_errmsg ( -1));
	shdr -> sh_name = 1;
	shdr -> sh_type = SHT_HASH ;
	shdr -> sh_flags = SHF_ALLOC ;
	shdr -> sh_entsize = 0;
	if (( scn = elf_newscn (e)) == NULL )
	{
		errx ( EX_SOFTWARE , " elf_newscn ()  failed : %s.",
	elf_errmsg ( -1));
	}

	if (( data = elf_newdata ( scn )) == NULL )
	{
		errx ( EX_SOFTWARE , " elf_newdata ()  failed : %s.",
	elf_errmsg ( -1));
	}

	data -> d_align = 1;
	data -> d_buf = string_table ;
	data -> d_off = 0LL;
	data -> d_size = sizeof ( string_table );
	data -> d_type = ELF_T_BYTE ;
	data -> d_version = EV_CURRENT ;

	if (( shdr = elf32_getshdr ( scn )) == NULL )
	{
		errx ( EX_SOFTWARE , " elf32_getshdr ()  failed : %s.",
	elf_errmsg ( -1));
	}

	shdr -> sh_name = 6;
	shdr -> sh_type = SHT_STRTAB ;
	shdr -> sh_flags = SHF_STRINGS | SHF_ALLOC ;
	shdr -> sh_entsize = 0;

	elf_setshstrndx(e, elf_ndxscn(scn));

	if ( elf_update (e, ELF_C_NULL ) < 0)
	{
		errx ( EX_SOFTWARE , " elf_update ( NULL )  failed : %s.",
	elf_errmsg ( -1));
	}
	phdr -> p_type = PT_PHDR ;
	phdr -> p_offset = ehdr -> e_phoff ;
	phdr -> p_filesz = elf32_fsize ( ELF_T_PHDR , 1, EV_CURRENT );
	( void ) elf_flagphdr (e, ELF_C_SET , ELF_F_DIRTY );
	if ( elf_update (e, ELF_C_WRITE ) < 0)
	{
		errx ( EX_SOFTWARE , " elf_update ()  failed : %s.",
	elf_errmsg ( -1));
	}

	( void ) elf_end (e);
	( void ) close (fd );
	exit ( EX_OK );
	}


void WriteInstruction(AssemblerData s,char * d)
{
	/*creates a section and adds to it*/
}

void WriteElfData(AssemblerData s,char *d)
{
	/*creates data section and adds to it*/
}

int
elf_setshstrndx(Elf *e, size_t strndx)
{
	void *eh;
	int ec;

	if (e == NULL || e->e_kind != ELF_K_ELF ||
	    ((ec = e->e_class) != ELFCLASS32 && ec != ELFCLASS64) ||
	    ((eh = _libelf_ehdr(e, ec, 0)) == NULL)) {
		LIBELF_SET_ERROR(ARGUMENT, 0);
		return (0);
	}

	return (_libelf_setshstrndx(e, eh, ec, strndx));
}

